#include "coro/coro.hpp"
#include "pw_allocator/first_fit.h"

class Consumer {
public:
  Consumer()
      : m_task(coro::Task([](coro::Context &cx) -> coro::Awaitable {
          CO_EVT(producer, int, int);
          CO_EVT(consumer, pw::Status);
          while (1) {
            if (auto r = co_await *producer; r.ok()) {
              const auto &[d1, d2] = r.value();
              printf("get value: %d and %d\n", d1, d2);
              if (d1 % 3 == 0) {
                consumer(pw::Status::DataLoss());
              } else {
                consumer(pw::OkStatus());
              }
            } else {
              printf("wrong status %s\n", r.status().str());
              consumer(pw::Status::DataLoss());
            }
          }
        }(m_cx))) {

    coro::Runnable::Instance().Start(m_task);
  }
private:
  pw::async2::CoroOrElseTask m_task;
  inline static std::byte m_buff[512];
  inline static pw::allocator::FirstFitAllocator m_alloc{m_buff};
  inline static coro::Context m_cx{m_alloc};
};

class Producer {
public:
  Producer() : m_task(coro::Task(CoTask(m_cx))) {
    coro::Runnable::Instance().Start(m_task);
  }
  coro::Awaitable CoTask(coro::Context &cx) {
    using namespace std::chrono_literals;
    CO_EVT(es, int, int);
    CO_EVT(producer, int, int);
    CO_EVT(consumer, pw::Status);
    int i = 0;
    int j = i + 1;
    while (1) {
      co_await coro::SleepFor(1s);
      producer(++i, ++j);
      auto r = co_await *consumer;
      printf("producer: consumer received status: %s\n", r->str());
    }
  }

private:
  pw::async2::CoroOrElseTask m_task;
  inline static std::byte m_buff[512];
  inline static pw::allocator::FirstFitAllocator m_alloc{m_buff};
  inline static coro::Context m_cx{m_alloc};
};

int main() {
  puts("=========================begin test====================\n");
  Consumer c{};
  Producer s{};
  coro::Runnable::Instance().Join();
  puts("==========================end test======================\n");
  return 0;
}
